#include "scheduler.h"
#include "log.h"
#include "hook.h"

#include <execinfo.h>
#include <assert.h>

namespace learnco {

//static 全局变量 仅在该文件可见
static learnco::Logger::ptr g_logger = learnco::LoggerMgr::GetInstance()->getLogger("scheduler");
static Scheduler* t_scheduler = nullptr;

//构造器
Scheduler::Scheduler(size_t threads , const std::string& name, bool debug)
    :m_name(name)
    ,m_debug(debug)
    ,m_threadCount(threads){

        assert(threads>0);
        t_scheduler = this;

        Fiber::GetThis();
        learnco::Thread::SetName("main");

        //m_threadCount线程池线程个数
        assert(m_threadCount>=0);
}

//析构函数
Scheduler::~Scheduler() {
}



//构建线程池，启动run调度
void Scheduler::start() {

    //线程池
    m_threads.resize(m_threadCount);
    for(uint i=0; i < m_threadCount; i++){
        m_threads[i].reset(new Thread(std::bind(&Scheduler::run, this)
                            , m_name + "_" + std::to_string(i+1)));
        m_threadIds.push_back(m_threads[i]->getId());
    }
    
    //启动m_rootfiber, recv_mainthread记录当前上下文的位置
    //Fiber::context recv_mainthread;
    //m_rootFiber->swapIn(recv_mainthread);

    //等待线程池线程全部结束
    // for(uint i=0; i < m_threads.size(); i++ )
    // {
    //     m_threads[i]->join();
    // }
}

void Scheduler::stop(){
    //等待线程池线程全部结束
    for(uint i=0; i < m_threads.size(); i++ )
    {
        m_threads[i]->join();
    }
}


//调度
void Scheduler::run() {

    set_hook_enable(true);

    Fiber::ptr idle_fiber(new Fiber(std::bind(&Scheduler::v_idle, this)));
    Fiber::ptr task_fiber;

    while(true){
        task_fiber.reset();
        {
            MutexType::RaiiImpl lock(m_mutex);
            //从协程队列中取首个任务
            auto it = m_fibers.begin();
            if(it != m_fibers.end()){
                task_fiber = *it;
                m_fibers.erase(it);
            }
        }

        //检查fiber状态，TERM已结束 EXCEPT出现异常
        if(task_fiber && task_fiber->getState()!=Fiber::TERM && task_fiber->getState() != Fiber::EXCEPT){
            ++m_activeThreadCount;
            //当前线程去执行task_fiber，当task_fiber切出后，会回到这里
            task_fiber->swapIn();   
            --m_activeThreadCount;

            //如果还没有执行结束，放入任务队列，等待下一次调用
            if(task_fiber->getState()!=Fiber::TERM && task_fiber->getState()==Fiber::READY){
                this->scheduler(task_fiber);
            }
            else
            {
            }
            
        }
        // 任务队列为空，当前线程闲置，执行idle_fiber
        else{
            if(idle_fiber->getState()==Fiber::TERM || idle_fiber->getState()==Fiber::EXCEPT){
                if(m_debug) LEARNCO_LOG_INFO(g_logger) << "线程结束" ;
                break ;
            }
            else
            {
                /* code */
            }
        
            if(m_debug) LEARNCO_LOG_DEBUG(g_logger) << "m_fibers empty, activting idle_fiber : " << GetFiberID() ;
            ++m_idleThreadCount;
            idle_fiber->swapIn();  
            --m_idleThreadCount;
        }
    }

}

//当线程空闲时，跳转到该线程
//virtual 期望会被派生类重写
void Scheduler::v_idle(){
    //睡眠1s,再去检查队列是否还有任务
    do{
       if(m_debug) LEARNCO_LOG_DEBUG(g_logger) << "Scheduler::idle running" ;
    }while(0);
}

//往任务队列添加任务
void Scheduler::scheduler(learnco::Fiber::ptr f){
    MutexType::RaiiImpl lock(m_mutex);
    if(m_fibers.empty()){
        this->v_ticked();
    }
    m_fibers.push_back(f);
}


void Scheduler::scheduler(std::function<void()> cb){
    learnco::Fiber::ptr fb(new Fiber(cb));
    MutexType::RaiiImpl lock(m_mutex);
    if(m_fibers.empty()){
        this->v_ticked();
    }
    m_fibers.push_back(fb);
}



Scheduler* Scheduler::GetThis(){
    return t_scheduler;
}


}